﻿using System;
using System.Collections.Generic;
using System.Linq;
using StudyTracker.Domain.Entities;
using StudyTracker.Util;

namespace StudyTracker.Web.Models.Data
{
	public enum GraphType
	{
		Daily,
		Total
	}

	public class GraphModel
	{
		public static string DailyGraphTitle = "Units per Day";
		public static string TotalGraphTitle = "Total Units";

		public GraphType GraphType { get; private set; }
		public GraphContainer Graph { get; private set; }
		public bool HasStudySessions { get; private set; }

		public GraphModel(Student student, GraphType graphType)
		{
			GraphType = graphType;
			HasStudySessions = student.Subjects.SelectMany(x => x.StudySessions).Any();

			if (HasStudySessions)
			{
				Graph = new GraphContainer(student.Subjects, graphType);
			}
		}

		public GraphModel() { }

		public class GraphContainer
		{
			public string Title { get; private set; }
			public string YAxisTitle { get; private set; }
			public IEnumerable<GraphData> DataSeries { get; private set; }

			public GraphContainer(IEnumerable<Student.Subject> subjects, GraphType graphType)
			{
				switch (graphType)
				{
					case GraphType.Daily:
						Title = DailyGraphTitle;
						break;
					case GraphType.Total:
						Title = TotalGraphTitle;
						break;
				}

				YAxisTitle = Title.ToLower();

				DataSeries = subjects
					.Where(x => x.StudySessions.Any())
					.Select(x => new GraphData(x.Name, x.StudySessions, graphType));
			}

			public class GraphData
			{
				public string name { get; private set; }
				public double[][] data { get; private set; }

				public GraphData(string name, IList<Student.Subject.StudySession> studySessions, GraphType graphType)
				{
					this.name = name;

					switch (graphType)
					{
						case GraphType.Daily:
							data = CreateDailyData(studySessions);
							break;
						case GraphType.Total:
							data = CreateTotalData(studySessions);
							break;
					}
				}

				private static double[][] CreateDailyData(IEnumerable<Student.Subject.StudySession> studySessions)
				{
					IEnumerable<IGrouping<DateTime, Student.Subject.StudySession>> groupedSessions = studySessions
						.OrderBy(s => s.Date)
						.GroupBy(s => new DateTime(s.Date.Year, s.Date.Month, s.Date.Day));

					IGrouping<DateTime, Student.Subject.StudySession> firstSession = groupedSessions.First();
					IGrouping<DateTime, Student.Subject.StudySession> lastSession = groupedSessions.Last();
					DateTime firstDateTime = new DateTime(firstSession.Key.Year, firstSession.Key.Month, firstSession.Key.Day);

					int totalDays = (lastSession.Key - firstSession.Key).Days + 1;

					double[][] data = new double[totalDays][];

					for (int i = 0; i < totalDays; i++)
					{
						DateTime currentDateTime = firstDateTime.AddDays(i);

						IGrouping<DateTime, Student.Subject.StudySession> session = groupedSessions
							.Where(s => new DateTime(s.Key.Date.Year, s.Key.Date.Month, s.Key.Date.Day) == currentDateTime)
							.SingleOrDefault();

						double count = session != null
										? session.Aggregate<Student.Subject.StudySession, double>(0, (current, studySession) => current + studySession.Amount)
										: 0;

						data[i] = new double[2];
						data[i][0] = currentDateTime.ToUnixTimestamp();
						data[i][1] = count;
					}

					return data;
				}

				private static double[][] CreateTotalData(IList<Student.Subject.StudySession> studySessions)
				{
					double[][] data = new double[studySessions.Count()][];
					double runningTotal = 0;

					for (int i = 0; i < studySessions.Count(); i++)
					{
						runningTotal = runningTotal + studySessions[i].Amount;

						data[i] = new double[2];
						data[i][0] = studySessions[i].Date.ToUnixTimestamp();
						data[i][1] = runningTotal;
					}

					return data;
				}
			}
		}
	}
}